package cn.ltgodm.template.user.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import cn.ltgodm.template.common.annotation.DataScope;
import cn.ltgodm.template.common.constant.CacheConstants;
import cn.ltgodm.template.common.constant.Constants;
import cn.ltgodm.template.common.constant.UserConstants;
import cn.ltgodm.template.common.exception.ServiceException;
import cn.ltgodm.template.common.exception.user.UserPasswordNotMatchException;
import cn.ltgodm.template.common.exception.user.UserPasswordRetryLimitExceedException;
import cn.ltgodm.template.common.utils.*;
import cn.ltgodm.template.common.domain.vo.UserVO;
import cn.ltgodm.template.common.domain.entity.Logininfor;
import cn.ltgodm.template.common.content.AuthenticationContextHolder;
import cn.ltgodm.template.user.domain.entity.UserRole;
import cn.ltgodm.template.user.domain.param.UserParam;
import cn.ltgodm.template.user.domain.param.UserQueryParam;
import cn.ltgodm.template.user.service.LogininforService;
import cn.ltgodm.template.user.service.UserRoleService;
import cn.ltgodm.template.user.service.UserService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import cn.ltgodm.template.common.domain.entity.User;
import cn.ltgodm.template.user.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * @author wenpeng
 * @description 针对表【lt_user(用户信息表)】的数据库操作Service实现
 * @createDate 2024-03-17 21:19:39
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private LogininforService logininforService;
    @Autowired
    private UserRoleService userRoleService;

    @Value(value = "${user.password.maxRetryCount}")
    private int maxRetryCount;

    @Value(value = "${user.password.lockTime}")
    private int lockTime;

    /**
     * 校验用户密码
     *
     * @param user 用户信息
     */
    public void validate(UserVO user) {
        Authentication usernamePasswordAuthenticationToken = AuthenticationContextHolder.getContext();
        String username = usernamePasswordAuthenticationToken.getName();
        String password = usernamePasswordAuthenticationToken.getCredentials().toString();

        //获取错误次数
        Integer retryCount = redisCache.getCacheObject(getCacheKey(username));

        //根据错误次数进行判断
        if (ObjectUtil.isNull(retryCount)) {
            retryCount = 0;
        }
        if (retryCount.compareTo(maxRetryCount) >= 0) {
            //到达最大锁定时间
            throw new UserPasswordRetryLimitExceedException(maxRetryCount, lockTime);
        } else if (!matches(user, password)) {
            //密码错误
            throw new UserPasswordNotMatchException();
        } else {
            //正确，清楚缓存错误密码次数
            clearLoginRecordCache(username);
        }
    }

    /**
     * 获取缓存Key
     *
     * @param username 用户名
     * @return 返回缓存用户密码错误缓存key
     */
    private String getCacheKey(String username) {
        return CacheConstants.PWD_ERR_CNT_KEY + username;
    }

    /**
     * 通过用户名获取用户
     *
     * @param username 用户名
     * @return 用户信息
     */
    @Override
    public UserVO selectUserByUserName(String username) {
        return userMapper.selectUserVoByUserName(username);
    }


    /**
     * 清楚用户登录时，密码错误的缓存次数
     *
     * @param username 用户名
     */
    private void clearLoginRecordCache(String username) {
        if (redisCache.hasKey(getCacheKey(username))) {
            redisCache.deleteObject(getCacheKey(username));
        }
    }


    /**
     * 校验密码
     *
     * @param user        用户
     * @param rawPassword 未加密的密码
     * @return 返回校验结果
     */
    private boolean matches(UserVO user, String rawPassword) {
        return SecurityUtils.matchesPassword(rawPassword, user.getPassword());
    }


    /**
     * 记录用户登录记录
     *
     * @param username 用户名
     * @param status   状态
     * @param message  信息
     * @param args     参数
     */
    @Override
    public void recordLogininfor(final String username, final String status, final String message, final Object... args) {
        //获取用户登录客户端信息
        final UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
        final String ip = IpUtils.getIpAddr();

        String address = AddressUtils.getRealAddressByIP(ip);
        StringBuilder s = new StringBuilder();
        s.append(LogUtils.getBlock(ip));
        s.append(address);
        s.append(LogUtils.getBlock(username));
        s.append(LogUtils.getBlock(status));
        s.append(LogUtils.getBlock(message));
        // 打印信息到日志
        log.info(s.toString(), args);
        // 获取客户端操作系统
        String os = userAgent.getOs().getName();
        // 获取客户端浏览器
        String browser = userAgent.getBrowser().getName();
        // 封装对象
        Logininfor logininfor = new Logininfor();
        logininfor.setUserName(username);
        logininfor.setIpaddr(ip);
        logininfor.setLoginLocation(address);
        logininfor.setBrowser(browser);
        logininfor.setOs(os);
        logininfor.setMsg(message);
        logininfor.setLoginTime(new Date());
        // 日志状态
        if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) {
            logininfor.setStatus(Constants.SUCCESS);
        } else if (Constants.LOGIN_FAIL.equals(status)) {
            logininfor.setStatus(Constants.FAIL);
        }
        // 插入数据
        logininforService.save(logininfor);
    }

    /**
     * 分页查询用户列表信息
     *
     * @param userQueryParam 查询参数
     * @return 返回用户分页信息
     */
    @Override
    @DataScope(deptAlias = "d", userAlias = "u")
    public IPage<UserVO> selectUserList(UserQueryParam userQueryParam) {
        //构造查询条件
        LambdaQueryWrapper<User> lqw = getQueryWrapper(userQueryParam);
        IPage<UserVO> page = new Page<>(userQueryParam.getPageNum(), userQueryParam.getPageSize());
        //查询
        return userMapper.selectUserList(page, lqw, userQueryParam.getParams());
    }

    /**
     * 校验用户的数据获取范围
     *
     * @param userId 用户id
     */
    @Override
    public void checkUserDataScope(Long userId) {
        if (!UserVO.isAdmin(SecurityUtils.getUserId())) {
            UserQueryParam userQueryParam = new UserQueryParam();
            userQueryParam.setUserId(userId);
            IPage<UserVO> page = SpringUtils.getAopProxy(this).selectUserList(userQueryParam);
            if (ObjectUtil.isNull(page) || CollectionUtil.isNotEmpty(page.getRecords())) {
                throw new ServiceException("没有权限访问用户数据！");
            }
        }
    }


    /**
     * 通过userID获取用户信息
     *
     * @param userId 用户id
     * @return 返回用户信息
     */
    @Override
    public UserVO selectUserById(Long userId) {
        return userMapper.selectUserById(userId);
    }

    /**
     * 校验用户名是否唯一
     *
     * @param user 用户信息
     * @return 返回校验结果
     */
    @Override
    public boolean checkUserNameUnique(User user) {
        Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId();
        User userDB = userMapper.checkUserNameUnique(user.getUserName());
        if (ObjectUtil.isNotNull(userDB) && userDB.getUserId().longValue() != userId.longValue()) {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
    }

    /**
     * 校验手机号是否唯一
     *
     * @param user 用户信息
     * @return 返回校验结果
     */
    @Override
    public boolean checkPhoneUnique(User user) {
        Long userId = ObjectUtil.isNull(user.getUserId()) ? -1L : user.getUserId();
        User userDB = userMapper.checkPhoneUnique(user.getPhonenumber());
        if (ObjectUtil.isNotNull(userDB) && userDB.getUserId().longValue() != userId.longValue()) {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
    }

    /**
     * 校验邮箱是否唯一
     *
     * @param user 用户信息
     * @return 返回校验结果
     */
    @Override
    public boolean checkEmailUnique(User user) {
        Long userId = ObjectUtil.isNull(user.getUserId()) ? -1L : user.getUserId();
        User userDB = userMapper.checkEmailUnique(user.getEmail());
        if (ObjectUtil.isNotNull(userDB) && userDB.getUserId().longValue() != userId.longValue()) {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
    }

    /**
     * 新增用户
     *
     * @param user 用户信息
     * @return 返回修改的行数
     */
    @Override
    @Transactional
    public int insertUser(UserParam user) {
        //新增用户信息
        int insert = userMapper.insert(user);
        //新增用户角色信息
        insertUserRole(user);
        return insert;
    }

    /**
     * 校验用户是否允许此操作
     *
     * @param user 用户信息
     */
    @Override
    public void checkUserAllowed(UserParam user) {
        if (StringUtils.isNotNull(user.getUserId()) && user.isAdmin()) {
            throw new ServiceException("不允许操作超级管理员用户");
        }
    }

    /**
     * 修改用户信息
     *
     * @param user 用户信息
     * @return 返回修改行数
     */
    @Override
    @Transactional
    public int updateUser(UserParam user) {
        Long userId = user.getUserId();
        //删除用户与角色的联系
        userRoleService.deleteUserRoleByUserId(userId);
        //新增用户与角色的联系
        insertUserRole(user);

        return userMapper.updateById(user);
    }

    /**
     * 删除用户信息
     *
     * @param userIds 用户id
     * @return 返回操作行数
     */
    @Override
    @Transactional
    public boolean deleteUser(Long[] userIds) {
        for (Long userId : userIds) {
            UserParam userParam = new UserParam();
            userParam.setUserId(userId);
            checkUserAllowed(userParam);
        }
        //删除用户关联角色
        userRoleService.deleteUserRole(userIds);
        List<Long> idList = Arrays.asList(userIds);
        return removeByIds(idList);
    }

    /**
     * 重置用户密码
     *
     * @param user 用户信息
     * @return 返回修改行数
     */
    @Override
    public int resetPwd(User user) {
        User userDB = new User();
        userDB.setUserId(user.getUserId());
        userDB.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
        userDB.setUpdateBy(SecurityUtils.getLoginUser().getUsername());
        return userMapper.updateById(userDB);
    }

    /**
     * 修改用户状态信息
     *
     * @param user 用户信息
     * @return 用户状态
     */
    @Override
    public int updateUserStatus(User user) {
        User userDB = new User();
        userDB.setUserId(user.getUserId());
        userDB.setStatus(user.getStatus());
        userDB.setUpdateBy(SecurityUtils.getLoginUser().getUsername());
        return userMapper.updateById(userDB);
    }

    /**
     * 用户注册
     *
     * @param userDB 用户信息
     */
    @Override
    public int registerUser(User userDB) {
        return userMapper.insert(userDB);
    }

    /**
     * 新增用户角色信息
     *
     * @param user 用户信息
     */
    private void insertUserRole(UserParam user) {
        insertUserRole(user.getUserId(), user.getRoleIds());
    }

    /**
     * 执行新增用户角色信息
     *
     * @param userId  用户id
     * @param roleIds 角色id列表
     */
    private void insertUserRole(Long userId, Long[] roleIds) {
        if (StringUtils.isNotEmpty(roleIds)) {
            // 新增用户与角色管理
            List<UserRole> list = new ArrayList<UserRole>(roleIds.length);
            for (Long roleId : roleIds) {
                UserRole userRole = new UserRole();
                userRole.setUserId(userId);
                userRole.setRoleId(roleId);
                list.add(userRole);
            }
            userRoleService.saveBatch(list);
        }
    }

    /**
     * 构造查询参数
     *
     * @param userQueryParam 查询参数
     * @return 查询构造器
     */
    private LambdaQueryWrapper<User> getQueryWrapper(UserQueryParam userQueryParam) {
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
        lqw
                .eq(ObjectUtil.isNotEmpty(userQueryParam.getUserId()), User::getUserId, userQueryParam.getUserId())
                .eq(StringUtils.isNotEmpty(userQueryParam.getUserName()), User::getUserName, userQueryParam.getUserName())
                .eq(StringUtils.isNotEmpty(userQueryParam.getStatus()), User::getStatus, userQueryParam.getStatus());
        return lqw;
    }


}




